package com.acme.chat.client;

import static com.acme.chat.settings.Settings.LOCALE_BUNDLE;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import org.apache.log4j.Logger;

import com.acme.chat.message.ChatMessage;
import com.acme.chat.message.Message;
import com.acme.chat.message.MessageType;

public class Client implements ChatClient {
    public static final String DEFAULT_NAME = "Anonym";
    public static final String DEFAULT_ROOM = "1";

    private static Logger chatLogger = Logger.getLogger(ChatClient.class.getName());

    private ObjectInputStream sInput;
    private ObjectOutputStream sOutput;
    private Socket socket;
    private ClientGUI cg;
    private String server;
    private String name;
    private int port;
    private String room;

    public Client(String server, int port, String room, String username) {
        this(server, port, room, username, null);
    }

    public Client(String server, int port, String room, String name, ClientGUI cg) {
        this.server = server;
        this.port = port;
        this.room = checkRoom(room);
        this.name = deleteWhitespaces(name);
        this.cg = cg;
    }

    private String checkRoom(String checkedRoom) {
        if (checkedRoom == null || "".equals(checkedRoom.trim())) { return DEFAULT_ROOM; }
        return checkedRoom.trim();
    }

    private static String deleteWhitespaces(String name) {
        if (name == null || "".equals(name.trim())) { return DEFAULT_NAME; }
        return name.trim();
    }

    public boolean start() {
        try {
            socket = new Socket(server, port);
        } catch (Exception e) {
            display(LOCALE_BUNDLE.getString("ErrConnectToServer"));
            return false;
        }

        String msg = LOCALE_BUNDLE.getString("ConnectionAccepted") + socket.getInetAddress() + ":" + socket.getPort();
        msg = msg + ", room: " + room + "\n";
        display(msg);

        try {
            sInput = new ObjectInputStream(socket.getInputStream());
            sOutput = new ObjectOutputStream(socket.getOutputStream());
        } catch (IOException e) {
            display(LOCALE_BUNDLE.getString("ExcCreateIOStream"));
            return false;
        }

        try {
            sOutput.writeObject(name);
            sOutput.writeObject(room);
        } catch (IOException e) {
            display(LOCALE_BUNDLE.getString("ExcLogin"));
            disconnect();
            return false;
        }
        new Client.ListenFromServer().start();
        return true;
    }

    private void display(String message) {
        if (cg == null) {
            chatLogger.info(message);
        } else {
            cg.append(message);
        }
    }

    @Override
    public boolean sendMessage(Message message) {
        try {
            if (message.getMessage().length() > Message.MAX_MESSAGE_LENGTH) {
                throw new IllegalArgumentException(LOCALE_BUNDLE.getString("ErrToLongMessage") + Message.MAX_MESSAGE_LENGTH + "\n");
            }
            if ((message.getType() == MessageType.MESSAGE) && message.getMessage().isEmpty()) {
                return false;
            }
            sOutput.writeObject(message);
            return true;
        } catch (IOException e) {
            display(LOCALE_BUNDLE.getString("ExcWritingToServer"));
            return false;
        } catch (IllegalArgumentException e) {
            display(e.getMessage());
            return false;
        }
    }

    @Override
    public void disconnect() {
        if (sOutput != null) {
            sendMessage(new ChatMessage(MessageType.LOGOUT));
        }
        close();
    }

    private void close() {
        try {
            if (socket != null) {
                socket.close();
                socket = null;
            }
        } catch (Exception e) {
            chatLogger.info(e.getMessage());
        }
    }

    @Override
    public String getRoom() {
        return room;
    }

    @Override
    public String getName() {
        return name;
    }

    private class ListenFromServer extends Thread {

        @Override
        public void run() {
            while (true) {
                try {
                    Message message = (Message) sInput.readObject();
                    display(message.getMessage());
                } catch (IOException e) {
                    display(LOCALE_BUNDLE.getString("ServerCloseConnection"));
                    if (cg != null) {
                        cg.onDisconnectActions();
                    }
                    close();
                    break;
                } catch (ClassNotFoundException e) {
                    chatLogger.info(e.getMessage());
                }
            }
        }
    }

    @Override
    public boolean changeRoom(String room) {
        if (!this.room.equals(room)) {
            this.room = room;
            return true;
        }
        return false;
    }
}